home *** CD-ROM | disk | FTP | other *** search
/ Aminet 40 / Aminet 40 (2000)(Schatztruhe)[!][Dec 2000].iso / Aminet / util / cdity / MRQ.lha / MRQ / Source / Requesters.c < prev    next >
C/C++ Source or Header  |  2000-10-16  |  29KB  |  982 lines

  1. /* Requesters.c
  2. ** This is where the real work is done!
  3. ** - Functions patched into intuition and reqtools.library
  4. ** - Functions to build a MUI requester according to an
  5. **   MRQReqMessage structure
  6. ** - Message handling
  7. **
  8. ** ©1997-2000 by Matthias.Bethke <Matthias.Bethke@gmx.net>
  9. ** You are free to modify this source or use parts of it in your
  10. ** own programs as long as they are distributed as freeware.
  11. */
  12.  
  13. /* $Id: Requesters.c 1.4 2000/10/15 18:06:08 msbethke Exp msbethke $
  14. **
  15. ** $Log: Requesters.c $
  16. ** Revision 1.4  2000/10/15 18:06:08  msbethke
  17. ** Structured the button generation part a little better
  18. ** (actually this should have long gone into a MUI class of its own :-( )
  19. ** Now uses my own reentrant strtok()-function, not yet everywhere though.
  20. **
  21. ** Revision 1.3  2000/03/30 23:18:43  msbethke
  22. ** Changed NewImage.mcc -> Guigfx.mcc
  23. **
  24. ** Revision 1.2  2000/01/25 17:30:22  msbethke
  25. ** Adapted to new header names
  26. ** Fixed obsolete NewImage.mcc tag names/structures
  27. **
  28. ** Revision 1.1  2000/01/25 16:57:49  msbethke
  29. ** Initial revision
  30. **
  31. */
  32.  
  33.  
  34. #include <proto/utility.h>
  35. #include <exec/memory.h>
  36. #include <exec/alerts.h>
  37. #include <intuition/intuition.h>
  38. #include <intuition/intuitionbase.h>
  39. #include <libraries/reqtools.h>
  40. #include <lib/mb_utils.h>
  41. #include <MUI/Guigfx_mcc.h>
  42. #include <MUI/TransferAnim_mcc.h>
  43. #include <math.h>
  44. #include <ctype.h>
  45.  
  46. #include "muistuff.h"
  47. #include "mrq.h"
  48. #include "config.h"
  49. #include "gfxfiles.h"
  50. #include "MRQasm.h"
  51. #include "mui_macros.h"
  52.  
  53. /************************************************************************/
  54. /* local protos                                                         */
  55. /************************************************************************/
  56. static struct MRQReqMessage *CreateMsgAndPort(ULONG);
  57. static STRPTR GetCallingProgram(APTR);
  58. static void FreeMsgAndPort(struct MRQReqMessage*);
  59. static BOOL CheckAvoidTask(struct Task*);
  60. static STRPTR FormatButtonText(STRPTR, APTR);
  61. static struct MRQEventClass *AnalyzeReqText(STRPTR,STRPTR);
  62. static APTR CreateMRQButton(STRPTR, BOOL, BOOL, struct Screen*, UBYTE, BOOL);
  63. static APTR CreateImageButton(STRPTR,struct MRQImageButton*, struct Screen*, UBYTE, BOOL);
  64. static APTR CreateTextButton(STRPTR, UBYTE, BOOL);
  65. static WORD PreParseButtonText(STRPTR, UBYTE, BOOL, STRPTR*, STRPTR*);
  66.  
  67. /*****************************************************************************/
  68.  
  69. #define GADBUFSIZE (256)
  70.  
  71. /*****************************************************************************/
  72.  
  73. LONG __asm __saveds EasyRequestArgsPatch(
  74.     register __a0 struct Window *ParentWindow,
  75.     register __a1 struct EasyStruct *es,
  76.     register __a2 ULONG *IDCMPptr,
  77.     register __a3 APTR ArgList)
  78. {
  79. static const struct EasyStruct easy = {sizeof(struct EasyStruct),0,"System error",
  80. "EasyRequestArgs() called by %s\nwith %s == NULL!","I see"};
  81. struct MRQReqMessage *msg;
  82. struct Window *pwindow;        // temp parent window
  83. struct Task *MyTask;
  84. STRPTR TaskName;
  85. LONG RCode = -1;
  86.  
  87.     MyTask = FindTask(NULL);
  88.     TaskName = MyTask->tc_Node.ln_Name ? MyTask->tc_Node.ln_Name : "<UNKNOWN>";
  89.  
  90.     pwindow = ParentWindow;
  91.  
  92. //    if((MyTask->tc_Node.ln_Type == NT_PROCESS) && (!pwindow))
  93. //        pwindow = ((struct Process*)MyTask)->pr_WindowPtr;
  94.  
  95.     if(pwindow == (struct Window*)-1) pwindow = NULL;
  96.  
  97.     prdebug("EasyRequestArgs patch entered by '%s'\n",TaskName);
  98.  
  99.     if(CheckAvoidTask(MyTask) || (!AppActive))
  100.     {
  101.         return CallOldERA(ParentWindow,es,IDCMPptr,ArgList,IntuitionBase); // use standard intuition
  102.     }
  103.  
  104.     if(!es->es_GadgetFormat)
  105.     {
  106.         EasyRequest(ParentWindow,(struct EasyStruct*)&easy,NULL,TaskName,"es_GadgetFormat");
  107.         return -1L;
  108.     }
  109.     if(!es->es_TextFormat)
  110.     {
  111.         EasyRequest(ParentWindow,(struct EasyStruct*)&easy,NULL,TaskName,"es_TextFormat");
  112.         return -1L;
  113.     }
  114.  
  115.     if(msg = CreateMsgAndPort(IDCMPptr?*IDCMPptr:0L))
  116.     {
  117.         /* fill message structure */
  118.         msg->mrm_Easy            = es;
  119.         msg->mrm_ParentWin    = pwindow;
  120.         msg->mrm_ArgList        = ArgList;
  121.         msg->mrm_CalledFrom    = CALLER_EASYREQUESTARGS;
  122.         msg->mrm_DefaultAnswer = 1;
  123.  
  124.         PutMsg(MainPort,(struct Message*)msg);
  125.         prdebug("Message sent\n");
  126.         WaitPort(msg->mrm_Message.mn_ReplyPort);
  127.         GetMsg(msg->mrm_Message.mn_ReplyPort);
  128.         prdebug("Received reply\n");
  129.  
  130.         RCode = msg->mrm_RCode;
  131.         if(msg->mrm_FatalError)
  132.         {
  133.             prdebug("Fatal error in MUI requester code - using original function!\n");
  134.             RCode = CallOldERA(ParentWindow,es,IDCMPptr,ArgList,IntuitionBase);
  135.         }
  136.         FreeMsgAndPort(msg);
  137.     }
  138.     prdebug("EasyRequestArgsPatch() result: %ld\n",RCode);
  139.     return RCode;
  140. }
  141.  
  142.  
  143. /*****************************************************************************/
  144.  
  145. ULONG __asm __saveds rtEZRequestAPatch(
  146.     register __a1 char *bodyfmt,
  147.     register __a2 char *gadfmt,
  148.     register __a3 struct rtReqInfo *reqinfo,
  149.     register __a5 APTR argarray,                    // usually a4 - requires asm stub!!!
  150.     register __a0 struct TagItem *taglist)
  151. {
  152. struct MRQReqMessage *msg;
  153. struct Window *pwindow=NULL;
  154. struct Task *MyTask;
  155. STRPTR TaskName;
  156. ULONG RCode = -1;
  157. ULONG IDCMPflags=0, IDCMPsaved=0, ReqFlags=0;
  158. BOOL WaitPointer=FALSE, LockWindow=FALSE;
  159. struct TagItem *tlist, *tag;
  160. STRPTR ReqTitle=NULL;
  161. UBYTE rtUnderscore=0, DefaultAnswer=1;
  162.  
  163.     MyTask = FindTask(NULL);
  164.     TaskName = MyTask->tc_Node.ln_Name ? MyTask->tc_Node.ln_Name : "<UNKNOWN>";
  165.  
  166.     if(taglist)    pwindow = (struct Window*)GetTagData(RT_Window,0,taglist);
  167.  
  168. //    if((MyTask->tc_Node.ln_Type == NT_PROCESS) && (!pwindow))
  169. //        pwindow = ((struct Process*)MyTask)->pr_WindowPtr;
  170.  
  171.     if(pwindow == (struct Window*)-1) pwindow=NULL;
  172.  
  173.     prdebug("rtEZRequestA patch entered by '%s'\n",TaskName);
  174.  
  175.     /* avoid certain tasks, buttonless/asynchronous requesters, and honor AppActive */
  176.     if(CheckAvoidTask(MyTask) ||
  177.         !(AppActive && gadfmt) ||
  178.         FindTagItem(RT_ReqHandler,taglist))
  179.     {
  180.         prdebug("Requester not MUIfiable - calling original function!\n");
  181.         return CallOldrtEZRA(bodyfmt,gadfmt,reqinfo,argarray,taglist,ReqToolsBase);
  182.     }
  183.  
  184.     if(!bodyfmt)
  185.     {
  186.     struct EasyStruct easy = {sizeof(struct EasyStruct),0,"MRQ error","rtEZRequestA() called by %s\nwith bodyfmt == NULL!","I see"};
  187.  
  188.         EasyRequest(NULL,&easy,NULL,TaskName);
  189.         return -1L;
  190.     }
  191.  
  192.     if(reqinfo)
  193.     {
  194.         ReqTitle = reqinfo->ReqTitle;
  195.         WaitPointer = reqinfo->WaitPointer;
  196.         LockWindow = reqinfo->LockWindow;
  197.         ReqFlags = reqinfo->Flags;
  198.     }
  199.  
  200.     tlist = taglist;
  201.     while(tag = NextTagItem(&tlist))
  202.     {
  203.         switch(tag->ti_Tag)
  204.         {
  205.             case  RT_IDCMPFlags    : IDCMPflags = tag->ti_Data;
  206.                 prdebug("IDCMPflags wanted: %08.lx\n",IDCMPflags);
  207.                 break;
  208.             case  RT_WaitPointer    : WaitPointer = tag->ti_Data;
  209.                 break;
  210.             case  RT_LockWindow    : LockWindow = tag->ti_Data;
  211.                 break;
  212.             case RT_Underscore    : rtUnderscore = (UBYTE)tag->ti_Data;
  213.                 break;
  214.             case RTEZ_ReqTitle    : ReqTitle = (STRPTR)tag->ti_Data;
  215.                 break;
  216.             case RTEZ_Flags        :
  217.                 if(tag->ti_Data & EZREQF_CENTERTEXT)  ReqFlags |= MRQMF_CENTERTEXT;
  218.                 if(tag->ti_Data & EZREQF_NORETURNKEY) ReqFlags |= MRQMF_NORETURNKEY;
  219.                 break;
  220.             case RTEZ_DefaultResponse : DefaultAnswer = tag->ti_Data;
  221.                 break;
  222.         }
  223.     }
  224.  
  225.     if(pwindow)
  226.     {
  227.         if(WaitPointer || LockWindow)
  228.         {
  229.             SetWindowPointer(pwindow,WA_BusyPointer,TRUE,TAG_DONE);
  230.         }
  231.         if(LockWindow)
  232.         {
  233.             IDCMPsaved = pwindow->IDCMPFlags;
  234.             ModifyIDCMP(pwindow,0);                // turn off all IDCMP messages
  235.         }
  236.     }
  237.  
  238.     if(msg = CreateMsgAndPort(IDCMPflags))
  239.     {
  240.     struct EasyStruct es = {0};
  241.  
  242.         /* fake an EasyStruct :) */
  243.         es.es_TextFormat = bodyfmt;
  244.         es.es_GadgetFormat = gadfmt ? gadfmt : "OK";        // gadfmt may be NULL!
  245.         es.es_Title = ReqTitle;
  246.  
  247.         /* fill message structure */
  248.         msg->mrm_Easy                = &es;
  249.         msg->mrm_ArgList            = argarray;
  250.         msg->mrm_CalledFrom        = CALLER_RTEZREQUESTA;
  251.         msg->mrm_ParentWin        = pwindow;
  252.         msg->mrm_Flags                = ReqFlags;
  253.         msg->mrm_Underscore        = rtUnderscore;
  254.         msg->mrm_DefaultAnswer    = DefaultAnswer;
  255.  
  256.         PutMsg(MainPort,(struct Message*)msg);
  257.         prdebug("Message sent\n");
  258.         WaitPort(msg->mrm_Message.mn_ReplyPort);
  259.         GetMsg(msg->mrm_Message.mn_ReplyPort);
  260.         prdebug("Received reply\n");
  261.  
  262.         RCode = msg->mrm_RCode;
  263.         if(msg->mrm_FatalError)
  264.         {
  265.             prdebug("Fatal error in MUI requester code - using original function!\n");
  266.             RCode = CallOldrtEZRA(bodyfmt,gadfmt,reqinfo,argarray,taglist,ReqToolsBase);
  267.         }
  268.         FreeMsgAndPort(msg);
  269.     } else prdebug("Can't create message/port!\n");
  270.     if(pwindow)
  271.     {
  272.         if(WaitPointer || LockWindow) ClearPointer(pwindow);        // reqtools compatible
  273.         if(LockWindow) ModifyIDCMP(pwindow,IDCMPsaved);
  274.     }
  275.     prdebug("rtEZRequestAPatch() result: %ld\n",RCode);
  276.     return RCode;
  277.  
  278.  
  279. }
  280.  
  281. /*****************************************************************************
  282. ** GetCallingProgram()                                                       *
  283. ** Returns a pointer to the calling program's name (including directory),    *
  284. ** in a newly allocted buffer. Deallocate with FreeString() (mb_utils.lib)   *
  285. ** Parameters: pool : optional mempool to allocate buffer from               *
  286. ******************************************************************************/
  287. static STRPTR GetCallingProgram(APTR pool)
  288. {
  289. STRPTR ProgName, ProgDir, Path=NULL;
  290. BPTR DirLock;
  291.  
  292.     if(DirLock = GetProgramDir())
  293.     {
  294.     ULONG len;
  295.  
  296.         ProgDir    = mb_NameFromLock(pool,DirLock);
  297.         ProgName    = mb_GetProgramName(pool);
  298.         if(Path=AllocVecPooled(pool,len=strlen(ProgDir)+1+strlen(ProgName)+1))
  299.         {
  300.             strcpy(Path,ProgDir);
  301.             if(!(AddPart(Path,ProgName,len)))
  302.             {
  303.                 FreeVecPooled(pool,Path);
  304.                 Path = NULL;
  305.             }
  306.         }
  307.         FreeString(pool,ProgDir);
  308.         FreeString(pool,ProgName);
  309.     }
  310.     return Path;
  311. }
  312.  
  313. /*****************************************************************************
  314. ** CreateMsgAndPort()                                                        *
  315. ** Allocates a struct MRQReqMessage already filled with info common to all   *
  316. ** patches.                                                                  *
  317. ** Parameters: IDCMPFlags : IDCMP flags to terminate the requester           *
  318. ******************************************************************************/
  319.  
  320. static struct MRQReqMessage *CreateMsgAndPort(ULONG IDCMPflags)
  321. {
  322. struct MRQReqMessage *msg;
  323.  
  324.     /* allocate a message structure */
  325.     if(msg = AllocVec(sizeof(struct MRQReqMessage),MEMF_PUBLIC|MEMF_CLEAR)) // important for VMem systems!
  326.     {
  327.     struct MsgPort *RPort;
  328.  
  329.         /* create a reply port */
  330.         if(RPort = CreateMsgPort())
  331.         {
  332.             msg->mrm_Message.mn_ReplyPort = RPort;
  333.  
  334.             /* common to all patches, so let's do it here! */
  335.             msg->mrm_CallingProgram = GetCallingProgram(Config->mc_MemPool);
  336.  
  337.             if(IDCMPflags)
  338.             {
  339.             struct MUI_EventHandlerNode *meh;
  340.  
  341.                 /* allocate an EventHandlerNode if any IDCMP bits were requested */
  342.                 if(meh = AllocVec(sizeof(struct MUI_EventHandlerNode),MEMF_PUBLIC|MEMF_CLEAR))
  343.                 {
  344.                     meh->ehn_Priority    = 0;
  345.                     meh->ehn_Flags        = 0;
  346.                     meh->ehn_Events    = IDCMPflags;
  347.                     meh->ehn_Class        = NULL;
  348.                     msg->mrm_EventHandler = meh;
  349.                     prdebug("IDCMP flags requested (0x%08.lx), eventhandler initialized\n",IDCMPflags);
  350.                 } else Alert(AG_NoMemory);
  351.             }
  352.             return msg;
  353.         } else prdebug("Error creating reply port!\n");
  354.         FreeVec(msg);
  355.     } else Alert(AG_NoMemory);
  356.     return NULL;
  357. }
  358.  
  359.  
  360.  
  361. /*****************************************************************************
  362. ** FreeMsgAndPort()                                                          *
  363. ** Frees a struct MRQReqMessage allocated by CreateMsgAndPort()              *
  364. ******************************************************************************/
  365. static void FreeMsgAndPort(struct MRQReqMessage *msg)
  366. {
  367.     DeleteMsgPort(msg->mrm_Message.mn_ReplyPort);
  368.     if(msg->mrm_EventHandler) FreeVec(msg->mrm_EventHandler);
  369.     if(msg->mrm_CallingProgram) FreeString(Config->mc_MemPool,msg->mrm_CallingProgram);
  370.     FreeVec(msg);
  371. }
  372.  
  373.  
  374. /*****************************************************************************
  375. ** CheckAvoidTask()                                                          *
  376. ** Checks if the passed-in task matches the preconfigured AVOIDTASKS pattern *
  377. ** Parameters: task : task to check                                          *
  378. ** Returns TRUE if task matches pattern                                      *
  379. ******************************************************************************/
  380. static BOOL CheckAvoidTask(struct Task *task)
  381. {
  382.     if(ttVars.AvoidTasks)
  383.     {
  384.     UBYTE buf[256], *name;
  385.  
  386.         name = task->tc_Node.ln_Name;
  387.  
  388.         if(task->tc_Node.ln_Type == NT_PROCESS)
  389.         {
  390.             if(GetProgramName(buf,sizeof(buf)) && strlen(buf))
  391.             {
  392.                 name = buf;
  393.             }
  394.         }
  395.  
  396.         if(name)
  397.         {
  398.             if(MatchPattern(ttVars.AvoidTasks,name))
  399.             {
  400.                 prdebug("Task name \"%s\" matches AVOIDTASKS list!\n",name);
  401.                 return TRUE;
  402.             }
  403.         }
  404.     }
  405.     return FALSE;
  406. }
  407.  
  408. /*****************************************************************************/
  409.  
  410. /************************************************************************
  411. **
  412. ** MUI_EasyRequestArgs()
  413. ** Since MRQ V1.5 this is no longer a replacement function for
  414. ** EasyRequestArgs() but must be called by the MRQ process!
  415. **    Generalized for a couple of other requester functions, thus
  416. ** supporting features not present in EasyRequestArgs(), as of V1.7
  417. *************************************************************************/
  418. BOOL MUI_EasyRequestArgs(struct MRQReqMessage *msg)
  419. {
  420. STRPTR Title, TextFormat, GadgetFormat;
  421. UBYTE tbuf[1024];
  422. struct MRQEventClass *EventClass, DynEventClass;
  423. struct MRQImage *ReqImage;
  424. struct MUIP_Guigfx_ImageInfo IconImageInfo;
  425. struct Screen *scr;
  426. struct DiskObject *ProgramIcon=NULL;
  427. APTR ButtonArgs;
  428. BOOL IconShown=FALSE;
  429. struct StuffCharParams StuffCharParams;
  430. APTR Win, BtGrp, ReqImgObj;
  431.  
  432.     Title            = msg->mrm_Easy->es_Title;
  433.     TextFormat    = msg->mrm_Easy->es_TextFormat;
  434.     GadgetFormat= msg->mrm_Easy->es_GadgetFormat;
  435.  
  436.     if(ttVars.FrontmostScreen)
  437.     {
  438.     ULONG lock;
  439.  
  440.         lock = LockIBase(0);
  441.         scr = ((struct IntuitionBase*)(IntuitionBase))->FirstScreen;
  442.         UnlockIBase(lock);
  443.     } else
  444.     {
  445.         if(msg->mrm_ParentWin)
  446.         {
  447.             scr = msg->mrm_ParentWin->WScreen;
  448.         } else
  449.         {
  450.             msg->mrm_ReqScreen = scr = LockPubScreen(NULL);
  451.         }
  452.     }
  453.  
  454.     StuffCharParams.Position= 0;
  455.     StuffCharParams.Dest        = tbuf;
  456.     StuffCharParams.DestLen    = sizeof(tbuf);
  457.     ButtonArgs = RawDoFmt(TextFormat,msg->mrm_ArgList,&RDF_StuffChar,&StuffCharParams);
  458.  
  459.     EventClass = AnalyzeReqText(TextFormat,tbuf);
  460.  
  461.     if((EventClass == &Config->mc_DefClass) && msg->mrm_CallingProgram)
  462.     {
  463.         prdebug("default class - trying icon\n",msg->mrm_CallingProgram);
  464.         if(ProgramIcon = GetDiskObject(msg->mrm_CallingProgram))
  465.         {
  466.             prdebug("icon read\n");
  467.             if(ProgramIcon->do_Gadget.Flags & GFLG_GADGIMAGE)
  468.             {
  469.                 prdebug("image gadget found\n");
  470.                 memset(&DynEventClass,0,sizeof(struct MRQEventClass));            // clear
  471.                 IconImageInfo.Version = GUIGFX_IMAGEINFO_VERSION;
  472.                 IconImageInfo.Image = ProgramIcon->do_Gadget.GadgetRender;        // enter image pointer
  473.                 IconImageInfo.ColorTable = (ULONG*)MUIV_Guigfx_WBPalette;        // we want a new WB-like palette
  474.                 DynEventClass.mec_Image.mi_Object = &IconImageInfo;
  475.                 EventClass = &DynEventClass;
  476.                 IconShown = TRUE;
  477.             } else
  478.             {
  479.                 prdebug("Not an image gadget!\n");
  480.                 FreeDiskObject(ProgramIcon);
  481.             }
  482.         } else prdebug("Can't read icon for %s!\n",msg->mrm_CallingProgram);
  483.     }
  484.  
  485.     ReqImage = msg->mrm_ReqImage = &EventClass->mec_Image;
  486.  
  487.     prdebug("Final requester text: \"%s\"\nMatched for image: %s\n",
  488.         tbuf,(ReqImage->mi_Flags & MIF_FILENAME) ? ReqImage->mi_Object : (STRPTR)"<preloaded>");
  489.  
  490.     if(!Title) Title = (UBYTE*)((strchr(GadgetFormat,'|'))?"Request":"Information");
  491.  
  492.     if(ReqImage->mi_Flags & MIF_ANIMATION)
  493.     {
  494.         ReqImgObj = TransferAnimObject,
  495.                             MUIA_TransferAnim_File, ReqImage->mi_Object,
  496.                             MUIA_TransferAnim_FPS, 10,
  497.                         End;
  498.     } else
  499.     {
  500.         ReqImgObj = GuigfxObject,
  501.                             (ReqImage->mi_Flags & MIF_FILENAME) ?
  502.                                 MUIA_Guigfx_FileName :
  503.                                 IconShown ?
  504.                                     MUIA_Guigfx_ImageInfo :
  505.                                     MUIA_Guigfx_Picture,
  506.                             ReqImage->mi_Object,
  507.                             MUIA_Guigfx_ScaleMode, GGSMF_NONE,
  508.                             MUIA_Guigfx_Quality, ttVars.Quality,
  509.                             MUIA_Guigfx_Transparency, ttVars.Transparency ? GGTRF_MASK : 0,
  510.                         End;
  511.     }
  512.  
  513.     Win = MRQWindowObject,
  514.         MUIA_Window_Title, Title,
  515.         MUIA_Window_ScreenTitle, Title,
  516.         MUIA_Window_Screen, scr,
  517.         MUIA_Window_TopEdge, ttVars.MousedReq ? MUIV_Window_TopEdge_Moused : MUIV_Window_TopEdge_Centered,
  518.         MUIA_Window_LeftEdge, ttVars.MousedReq? MUIV_Window_LeftEdge_Moused: MUIV_Window_LeftEdge_Centered,
  519.         MUIA_Window_CloseGadget, FALSE,
  520.         MUIA_Window_SizeGadget, ttVars.Sizeable,
  521.         WindowContents, VGroup,
  522.             Child, HGroup,
  523.                 MUIA_Frame, ttVars.SingleFrame ? MUIV_Frame_Text : MUIV_Frame_None,
  524.                 Child, VGroup,
  525.                     GroupSpacing(0),
  526.                     MUIA_Frame, ttVars.SingleFrame ? MUIV_Frame_None : MUIV_Frame_Text,
  527.                     Child, RectangleObject,    End,
  528.                     Child, ReqImgObj,
  529.                     Child, RectangleObject, End,
  530.                 End,
  531.                 Child, VGroup,
  532.                     GroupSpacing(0),
  533.                     MUIA_Frame, ttVars.SingleFrame ? MUIV_Frame_None : MUIV_Frame_Text,
  534.                     MUIA_Background, MUII_TextBack,
  535.                     Child, VSpace(0),
  536.                     Child, TextObject,
  537.                         MUIA_Text_Contents, tbuf,
  538.                         MUIA_Text_PreParse, ((!strchr(tbuf,'\n'))    ||
  539.                                                     ttVars.Centered        ||
  540.                                                     (msg->mrm_Flags & MRQMF_CENTERTEXT)) ? "\33c" : "\33l",
  541.                     End,
  542.                     Child, VSpace(0),
  543.                 End,
  544.             End,
  545.             Child, MUI_MakeObject(MUIO_HBar,5),
  546.             Child, BtGrp = HGroup,
  547.                 MUIA_Group_SameWidth, ttVars.SameWidthButtons,
  548.                 MUIA_Group_SameHeight, TRUE,
  549.             End,
  550.         End,
  551.     End;
  552.     
  553.     if(IconShown)
  554.     {
  555.         FreeDiskObject(ProgramIcon);
  556.         prdebug("Freed icon\n");
  557.     }
  558.  
  559.     if(Win)
  560.     {
  561.     STRPTR GadgetsText;
  562.  
  563.         prdebug("Created window object @$%08lx\n",Win);
  564.  
  565.         msg->mrm_WindowObject = Win;
  566.  
  567.         set(Win,MRQWINDOWTAG_REQMESSAGE,msg);    /* keep this *before* the AddEventHandler call! */
  568.         if(msg->mrm_EventHandler)
  569.         {
  570.             msg->mrm_EventHandler->ehn_Object = Win;
  571.             DoMethod(Win,MUIM_Window_AddEventHandler,msg->mrm_EventHandler);
  572.             prdebug("IDCMP bits requested by caller: %08lx\n",msg->mrm_EventHandler->ehn_Events);
  573.         }
  574.  
  575.         if(GadgetsText = FormatButtonText(GadgetFormat, ButtonArgs))
  576.         {
  577.         char *ThisButton, *NextButton=GadgetsText;
  578.         APTR TButtonP;
  579.         BOOL defaultbutton, ButtonOK=TRUE;
  580.         int i;
  581.  
  582.             ThisButton = mb_strtok(&NextButton,"|");
  583.             for(    i=0;
  584.                     (ThisButton != NULL) && (i < MAXBUTTONS);
  585.                     ThisButton = mb_strtok(&NextButton,"|"),i++)
  586.             {
  587.                 /* try to create a new MUI button */
  588.                 defaultbutton = (msg->mrm_DefaultAnswer==i+1) || ((NextButton==NULL) && (msg->mrm_DefaultAnswer==0));
  589.                 TButtonP = CreateMRQButton(ThisButton,(i==0),(NextButton==NULL),scr,msg->mrm_Underscore,defaultbutton);
  590.  
  591.                 if(TButtonP)
  592.                 {
  593.                     DoMethod(BtGrp,OM_ADDMEMBER,TButtonP);
  594.  
  595.                     DoMethod(TButtonP,MUIM_Notify,MUIA_Pressed,FALSE,TButtonP,3,
  596.                                 MUIM_WriteLong,(NextButton||!i) ? i+1 : 0,&msg->mrm_RCode);
  597.  
  598.                     DoMethod(TButtonP,MUIM_Notify,MUIA_Pressed,FALSE,Application,2,
  599.                                 MUIM_Application_ReturnID,msg);
  600.  
  601.                     if(i<10)
  602.                     {
  603.                     ULONG fstring=MAKE_ID('f',0,0,0);
  604.                     
  605.                         if(i==9) fstring |= MAKE_ID(0,'1','0',0);
  606.                         else fstring |= (i+1+'0')<<16;
  607.  
  608.                         DoMethod(Win,MUIM_Notify,MUIA_Window_InputEvent,&fstring,
  609.                                     Win,3,MUIM_WriteLong,(NextButton||!i) ? i+1 : 0,&msg->mrm_RCode);
  610.  
  611.                         DoMethod(Win,MUIM_Notify,MUIA_Window_InputEvent,&fstring,
  612.                                     Application,2,MUIM_Application_ReturnID,msg);
  613.                     }
  614.  
  615.                     if(defaultbutton && (!(msg->mrm_Flags & MRQMF_NORETURNKEY)))
  616.                         set(Win,MUIA_Window_ActiveObject,TButtonP);
  617.                 } else
  618.                 {
  619.                     ButtonOK = FALSE;
  620.                     break;
  621.                 }
  622.             }
  623.  
  624.             if(ButtonOK)
  625.             {
  626.                 if(EventClass->mec_RxCmdString)
  627.                 {
  628.                     msg->mrm_ARexxPort= EventClass->mec_RxPortName ? EventClass->mec_RxPortName : (STRPTR)"PLAY";
  629.                     msg->mrm_ARexxCmd    = EventClass->mec_RxCmdString;
  630.                 }
  631.  
  632.                 DoMethod(Application,OM_ADDMEMBER,Win);
  633.  
  634.                 prdebug("Added window to application object\n");
  635.  
  636.                 DoMethod(Win,MUIM_Notify,MUIA_Window_InputEvent,"esc",
  637.                             Win,3,MUIM_WriteLong,0,&msg->mrm_RCode);
  638.                 DoMethod(Win,MUIM_Notify,MUIA_Window_InputEvent,"esc",
  639.                             Application,2,MUIM_Application_ReturnID,msg);
  640.  
  641.                 DoMethod(Win,MUIM_Notify,MUIA_Window_InputEvent,"lcommand v",
  642.                             Win,3,MUIM_WriteLong,1,&msg->mrm_RCode);
  643.                 DoMethod(Win,MUIM_Notify,MUIA_Window_InputEvent,"lcommand v",
  644.                             Application,2,MUIM_Application_ReturnID,msg);
  645.  
  646.                 DoMethod(Win,MUIM_Notify,MUIA_Window_InputEvent,"lcommand b",
  647.                             Win,3,MUIM_WriteLong,0,&msg->mrm_RCode);
  648.                 DoMethod(Win,MUIM_Notify,MUIA_Window_InputEvent,"lcommand b",
  649.                             Application,2,MUIM_Application_ReturnID,msg);
  650.  
  651.  
  652.                 set(Win,MUIA_Window_Open,TRUE);
  653.             } else
  654.             {
  655.                 prdebug("Error creating button objects!\n");
  656.                 msg->mrm_FatalError = TRUE;
  657.             }
  658.             FreeVec(GadgetsText);
  659.         } else 
  660.         {
  661.             prdebug("Can't format gadget text!\n");
  662.             msg->mrm_FatalError = TRUE;
  663.         }
  664.     } else
  665.     {
  666.         prdebug("Can't create window object!\n");
  667.         msg->mrm_FatalError = TRUE;
  668.     }
  669.  
  670.     if(!(ttVars.FrontmostScreen || msg->mrm_ParentWin)) UnlockPubScreen(NULL,scr);
  671.     return (BOOL)(!msg->mrm_FatalError);
  672. }
  673.  
  674. static STRPTR FormatButtonText(STRPTR GadgetFormat, APTR BtArgs)
  675. {
  676. STRPTR tbuf;
  677. struct StuffCharParams scp;
  678.  
  679.     if(tbuf = AllocVec(GADBUFSIZE,MEMF_ANY))
  680.     {
  681.         scp.Position= 0;
  682.         scp.Dest        = tbuf;
  683.         scp.DestLen = GADBUFSIZE;
  684.         RawDoFmt(GadgetFormat,BtArgs,&RDF_StuffChar,&scp);
  685.     }
  686.     return tbuf;
  687. }
  688.  
  689. /************************************************************************
  690. ** AnalyzeReqText()
  691. ** This short piece of code is MRQ's "core", the text analyzer. It
  692. ** matches all strings in all MRQ event class structures against the
  693. ** supplied requester texts and returns a pointer to the first class that
  694. ** matches.
  695. *************************************************************************/
  696. static struct MRQEventClass *AnalyzeReqText(STRPTR Text, STRPTR FormattedText)
  697. {
  698. struct MRQEventClass *class;
  699. struct MRQString *str;
  700. BOOL match;
  701. STRPTR tx;
  702.  
  703.     for(class=(struct MRQEventClass*)(Config->mc_ClassList.mlh_Head);
  704.         class->mec_Node.mln_Succ;
  705.         class = (struct MRQEventClass*)(class->mec_Node.mln_Succ))
  706.     {
  707.         for(str = (struct MRQString*)(class->mec_StringList.mlh_Head);
  708.             str->ms_Node.mln_Succ;
  709.             str = (struct MRQString*)(str->ms_Node.mln_Succ))
  710.         {
  711.             tx = (str->ms_Flags & MSF_FORMATTED) ? FormattedText : Text;
  712.  
  713.             if(str->ms_Flags & MSF_SUBSTRING)
  714.             {
  715.                 if(str->ms_Flags & MSF_NOCASE)
  716.                     match = (BOOL)mb_stristr(tx,str->ms_String);
  717.                 else
  718.                     match = (BOOL)strstr(tx,str->ms_String);
  719.             }
  720.             else if(str->ms_Flags & MSF_PATTERN)
  721.             {
  722.                 if(str->ms_Flags & MSF_NOCASE) match = MatchPatternNoCase(str->ms_String,tx);
  723.                 else match = MatchPattern(str->ms_String,tx);
  724.             }
  725.             else
  726.             {
  727.                 if(str->ms_Flags & MSF_NOCASE) match = (stricmp(str->ms_String,tx) == 0);
  728.                 else match = (strcmp(str->ms_String,tx) == 0);
  729.             }
  730.  
  731.             if(match) return class;
  732.         }
  733.     }
  734.     return &Config->mc_DefClass;
  735. }
  736.  
  737.  
  738. /************************************************************************
  739. ** CreateMRQButton()
  740. ** The one and only button creation function, delegates work to 
  741. ** CreateImageButton() and CreateTextButton().
  742. ** Parameters:
  743. ** first/last: indicate if button to make is the first or last in a
  744. **             requester
  745. ** scr       : screen to remap image stuff to, if any
  746. ** uscore    : character to be used as "underscore", this precedes any
  747. **             character that is to be underlined and used as a keyboard
  748. **             shortcut
  749. ** defbutton : indicates that this button is the default response button
  750. **             and its text should be printed in boldface
  751. ** 
  752. *************************************************************************/
  753. static APTR CreateMRQButton(STRPTR text, BOOL first, BOOL last, struct Screen *scr, UBYTE uscore, BOOL defbutton)
  754. {
  755. STRPTR PMatchText, PParseText;
  756. APTR ret=NULL;
  757. UWORD cchar=0;    // MUI button controlchar
  758.  
  759.  
  760.     if(uscore)
  761.     {
  762.         if((cchar = PreParseButtonText(text,uscore,defbutton,&PMatchText,&PParseText)) == -1) return NULL;
  763.     } else
  764.     {
  765.         PMatchText = PParseText = text;
  766.     }
  767.  
  768.     /* analyze text if requested, unless button is the first one and should be marked "OK" */
  769.     if(ttVars.IButtonsByText && (!(first && ttVars.SingleIsOK)))
  770.     {
  771.     int i;
  772.     STRPTR s;
  773.     struct MRQImageButton *ibuttons[3];
  774.     UBYTE stringbuf[256];
  775.     static BOOL ButtonUsed[3];
  776.  
  777.         ibuttons[0] = &Config->mc_IButton_Yes;
  778.         ibuttons[1] = &Config->mc_IButton_No;
  779.         ibuttons[2] = &Config->mc_IButton_Cancel;
  780.  
  781.         if(first) ButtonUsed[0] = ButtonUsed[1] = ButtonUsed[2] = FALSE;
  782.  
  783.         for(i=0; i<3; i++)        
  784.         {
  785.             if(ButtonUsed[i]) continue;        // don't use same button twice
  786.             mb_strlimcpy(stringbuf,ibuttons[i]->mib_Text,256);
  787.             s = strtok(stringbuf,"|");
  788.             while(s)
  789.             {
  790.                 if(mb_stristr(PMatchText,s))
  791.                 {
  792.                     prdebug("CreateMRQButton(): matched '%s' -> button #%ld\n",s,i);
  793.                     ButtonUsed[i] = TRUE;
  794.                     ret = CreateImageButton(PParseText,ibuttons[i],scr,cchar,defbutton);
  795.                 }
  796.                 s = strtok(NULL,"|");
  797.             }
  798.         }
  799.     } else
  800.     {
  801.         if(first && Config->mc_IButton_Yes.mib_Picture)
  802.         {
  803.             ret = CreateImageButton(PParseText,&Config->mc_IButton_Yes,scr,cchar,defbutton);
  804.         } else if(last && Config->mc_IButton_No.mib_Picture)
  805.         {
  806.             ret = CreateImageButton(PParseText,&Config->mc_IButton_No,scr,cchar,defbutton);
  807.         }
  808.     }
  809.     if(!ret) ret = CreateTextButton(PParseText,cchar,defbutton);
  810.     prdebug("CreateMRQButton: button created @$%08.lx\n",ret);
  811.  
  812.     if(uscore)
  813.     {
  814.         FreeVecPooled(Config->mc_MemPool,PMatchText);
  815.         FreeVecPooled(Config->mc_MemPool,PParseText);
  816.     }
  817.     set(ret,MUIA_CycleChain,1);
  818.     return ret;
  819. }
  820.  
  821.  
  822.  
  823. /************************************************************************
  824. ** CreateImageButton()
  825. ** Creates a button with text an an image on the left
  826. **
  827. ** ctlchar and defbutton: see CreateTextButton()
  828. *************************************************************************/
  829. static APTR CreateImageButton(STRPTR text, struct MRQImageButton *img, struct Screen *scr, UBYTE ctlchar, BOOL defbutton)
  830. {
  831. APTR button;
  832.  
  833.     button = 
  834.     VGroup,
  835.         ButtonFrame,
  836.         MUIA_InputMode, MUIV_InputMode_RelVerify,
  837.         MUIA_Background, MUII_ButtonBack,
  838.         Child, VSpace(0),
  839.         Child, HGroup,
  840.             Child, GuigfxObject,
  841.                 MUIA_Guigfx_Picture, img->mib_Picture,
  842.                 MUIA_Guigfx_ScaleMode, GGSMF_NONE,
  843.                 MUIA_Guigfx_Quality, ttVars.Quality,
  844.                 MUIA_Guigfx_Transparency, GGTRF_MASK,
  845.             End,
  846.             Child, TextObject,
  847.                 MUIA_Font, MUIV_Font_Button,
  848.                 MUIA_Text_Contents, text,
  849.                 MUIA_Text_PreParse, (defbutton ? "\33c\33b" : "\33c"),
  850.             End,
  851.         End,
  852.         Child, VSpace(0),
  853.     End;
  854.  
  855.     if(button && ctlchar)
  856.     {
  857.         set(button,MUIA_ControlChar,(LONG)ctlchar);
  858.     }
  859.  
  860.     return button;
  861. }
  862.  
  863. /************************************************************************
  864. ** CreateTextButton()
  865. ** Creates a button with only text in it.
  866. ** Optional: text rendering in boldface if defbutton is set; keyboard
  867. ** shortcut if ctlchar is set
  868. *************************************************************************/
  869. static APTR CreateTextButton(STRPTR text, UBYTE ctlchar, BOOL defbutton)
  870. {
  871. APTR button;
  872.  
  873.     if(Config->mc_IButton_Yes.mib_Picture ||
  874.         Config->mc_IButton_No.mib_Picture  ||
  875.         Config->mc_IButton_Cancel.mib_Picture)
  876.     {
  877.     /* use a height adaptive layout if imagebuttons are used */
  878.         button =
  879.         VGroup,
  880.             ButtonFrame,
  881.             MUIA_InputMode, MUIV_InputMode_RelVerify,
  882.             MUIA_Background, MUII_ButtonBack,
  883.             Child, VSpace(0),
  884.             Child, TextObject,
  885.                 MUIA_Font, MUIV_Font_Button,
  886.                 MUIA_Text_Contents, text,
  887.                 MUIA_Text_PreParse, (defbutton ? "\33c\33b" : "\33c"),
  888.             End,
  889.             Child, VSpace(0),
  890.         End;
  891.     } else
  892.     {
  893.         button =
  894.         TextObject,
  895.         ButtonFrame,
  896.         MUIA_InputMode, MUIV_InputMode_RelVerify,
  897.         MUIA_Background, MUII_ButtonBack,
  898.             MUIA_Font, MUIV_Font_Button,
  899.             MUIA_Text_Contents, text,
  900.             MUIA_Text_PreParse, (defbutton ? "\33c\33b" : "\33c"),
  901.         End;
  902.     }
  903.  
  904.     if(button && ctlchar)
  905.     {
  906.         set(button,MUIA_ControlChar,(LONG)ctlchar);
  907.     }
  908.  
  909.     return button;
  910. }
  911.  
  912.  
  913. /************************************************************************
  914. ** PreParseButtonText()
  915. ** Using a certain "underscore" character, this function scans the input
  916. ** text and generates two variants: one for pattern matching, without
  917. ** the underscore character(s), one for MUI, including formatting codes
  918. ** to set the font style to "underlined" for the one character following
  919. ** the first "underscore" and "boldface" for the default button.
  920. ** Returns the character to use for the button's MUIA_ControlChar or -1
  921. ** on failure
  922. *************************************************************************/
  923. static WORD PreParseButtonText(STRPTR text, UBYTE uscore, BOOL defbutton, STRPTR *PMatchText, STRPTR *PParseText)
  924. {
  925. ULONG slen;
  926. WORD hichar=0;
  927.  
  928.     slen = strlen(text)+1;
  929.  
  930.     if((*PMatchText = AllocVecPooled(Config->mc_MemPool,slen)) &&
  931.         (*PParseText = AllocVecPooled(Config->mc_MemPool,slen+6)))
  932.     {
  933.     STRPTR s,t;
  934.     UBYTE c;
  935.  
  936.         s = *PMatchText;
  937.         t = text;
  938.         do
  939.         {
  940.             c = *t++;
  941.             if(c != uscore) *s++ = c;    // omit <underscore> characters
  942.         } while(c);
  943.  
  944.         s = *PParseText;
  945.         t = text;
  946.         do
  947.         {
  948.             c = *t++;
  949.             if(c == uscore)
  950.             {
  951.                 *s++ = 0x1b;                // ESC
  952.                 *s++ = 'u';                    // underline
  953.                 *s++ = hichar = *t++;    // save underlined character
  954.                 *s++ = 0x1b;                // ESC
  955.                 *s++ = 'n';                    // normal
  956.                 if(defbutton)
  957.                 {
  958.                     *s++ = 0x1b;            // ESC
  959.                     *s++ = 'b';                // boldface
  960.                 }
  961.                     
  962.                 do
  963.                 {
  964.                     c = *t++;
  965.                     if(c != uscore) *s++ = c;    // just omit further <underscore> characters
  966.                 } while(c);
  967.             } else
  968.             {
  969.                 *s++ = c;
  970.             }
  971.         } while(c);
  972.  
  973.         hichar = tolower(hichar);
  974.  
  975.         return hichar;
  976.     } else
  977.     {
  978.         if(PMatchText) FreeVecPooled(Config->mc_MemPool,PMatchText);
  979.         return -1;
  980.     }
  981. }
  982.